Local + DeviceBroker
混合架构详细设计说明书 V3.0
统一业务入口,按物理会话治理连接;以租约授权、任务串行和恢复门禁支撑 120 穴位并发老化。
01 设计范围与结论
本说明书将老化软件仪器资源管理收敛为“统一业务入口 + Local 直连 + DeviceBroker 代理”的混合架构。120 个穴位拥有独立的 DUT 业务状态,但共享设备按物理连接、通道与协议会话仲裁。工程师通过发布配置选择执行位置,Recipe 使用逻辑能力与穴位映射,不直接选择 COM 口、IP 或进程。
Local 适合已分配给唯一执行进程、可独立连接的设备;Broker 适合跨进程共享的串口/USB/CAN、共用会话的多通道电源、RS485 总线及需要长连接和后台协议任务的设备。Broker 是设备 I/O 宿主,不承担 DUT 合格判定、整条 Recipe 或 MES 事务。
| 标识 | 设计不变量 | 实现位置 |
|---|---|---|
| INV-01 | 同一 PhysicalSessionKey 只有一个配置的 I/O 宿主;禁止 Local 与 Broker 同时打开 | 发布校验、连接守卫、操作系统/网络访问控制 |
| INV-02 | 预约授权与命令串行化分层;持有租约不等于可并发写同一物理会话 | ResourceManager + SessionGate |
| INV-03 | 租约到期先隔离和确认设备状态,禁止仅按 TTL 自动交给新持有者 | 仲裁状态机、恢复协调器 |
| INV-04 | 启动后冻结 Recipe、Config、Driver、DBC、UDS、映射 Hash | RunSnapshot、Broker Task |
| INV-05 | 所有写操作在实际 I/O 宿主执行入口校验;客户端校验仅为提示 | LocalExecutor / Broker Dispatcher |
| INV-06 | 命令已发送但结果未知时,记录 Unknown;禁止把超时当作未执行 | Task Journal、结果投递 |
| INV-07 | 安全急停和人员作业许可依赖独立硬件/PLC 联锁,软件租约不替代断能 | 设备电气与软件联锁接口 |
02 既有讨论、代码事实与修订依据
本次已读取本地 V2.1 设计说明书、多进程资源方案、配置/调试架构,以及下列 C# 文件。保留 V2.1 的 AgingRun / DUTExecution、版本快照、统一 DeviceService 和分层数据归档设计;将此前“按需 Broker”展开成可部署的混合方案。可检索的历史聊天仅提供部分近期内容,因此不将本文表述为全部历史讨论的逐字复现。
| 来源/证据 | 当前核查事实 | V3.0 处理 |
|---|---|---|
| Aging.Infrastructure/Core/ResourceManager.cs | ConcurrentDictionary 保存预约;_generation 为进程内字段;AcquireOperationAsync 未取得可贯穿 I/O 的排他锁;ValidateOwner 未检查 TTL | 新增持久化 fence、真实 SessionGate 和异步校验;不声称当前代码已解决操作互斥 |
| Contracts/ResourceOwnerToken.cs | Generation 可变,ExpiresAt 只读;构造器自动创建新 TokenId | 新增不可变 LeaseGrant DTO;持久化还原不能调用旧构造器生成新 token |
| Services/DeviceService.cs | 先 GetDriver,再 ValidateOwner,再调用驱动;使用 deviceId:Generic | 按运行快照解析通道资源键,执行入口再次校验;不允许绕开宿主 |
| Services/AutoDeviceFacade.cs | 释放异常仅记日志;旧接口为单设备作用域 | 增加释放失败状态、续期和原子组预约;保留兼容适配层但不宣称零改动 |
| ConnectionUtil/PipeClient.cs | SendMessage 在 WriteMessage 后立即 return true,读响应循环不可达;_serverName 未用于创建客户端流 | 只能复用代码框架;先补协议、响应关联、超时、连接生命周期和测试 |
| Plugin.DeviceConfig/Model/DeviceConfigModel.cs | 已有 DeviceDefinition、DeviceChannel、SlotDeviceMapping、DeviceConfigVersion、DeviceGroupTemplate | 保留导入来源;新增不可变配置快照,不把旧表主键当作完整历史版本模型 |
| Aging.Infrastructure.csproj | TargetFramework=net472,LangVersion=10.0,Microsoft.Bcl.AsyncInterfaces 包已引用 | 接口示例使用普通 DTO / Task;新包按目标框架验证后冻结 |
| MultiProcess_Instrument_Resource_Solution.html | 提出 DB Lease + Generation + 按需 Broker;含仅查询 DB 即保证物理安全等过强结论 | 将 TTL、软件 fencing、物理断能分开;维护交接必须经过恢复门禁 |
代码路径基准:D:\TestPlatform\TapProject\。项目文档来源为设计依据,当前 C# 文件为静态实现证据;本次未运行现有设备驱动或完整软件工程。来源清单和 SHA256 见第 22 章。
03 总体架构与依赖边界
| 模块 | 职责 | 明确边界 |
|---|---|---|
| RunService / RecipeScheduler | 创建 DUTExecution;异步等待;组织步骤、资源集合和恢复策略 | 不持有物理连接、不直接引用厂家 SDK |
| DeviceService / ResourceRouter | 逻辑能力解析、权限与快照检查、路由、结构化错误 | 不根据网络故障自动切换 Local/Broker |
| Local ResourceManager / LocalExecutor | 同机 arb.db 预约;命名 Mutex 连接守卫;会话队列与直连驱动 | 只控制配置分配给本进程的会话;TTL 过期不强抢 |
| BrokerClient / LeaseAuthorityClient | 命名管道请求、响应关联、事件订阅、断线查询 | 无真实驱动,不把客户端 UI 状态当授权 |
| DeviceBroker | 本域授权、任务调度、会话串行、驱动生命周期、遥测分发 | 不替代 Recipe、MES 或全平台全局锁服务 |
| DeviceManager / Driver | 依配置实例化、连接、自检、重连、协议解析和读回 | 不决定谁有生产/维护权限 |
| ConfigurationService | 草稿/发布/生效、拓扑冲突检查、版本分发 | 运行中的快照不可热覆盖 |
| Supervisor / RecoveryCoordinator | 宿主存活检查、隔离、设备核对、人工恢复许可 | 拉起进程不自动恢复输出 |
ResourceManager 与 DeviceManager 互不直接依赖,由应用协调器组合。Broker 内也采用同样边界,所有物理写入只能从 Dispatcher 经 SessionGate 到 Driver。监控读取缓存快照,不额外建立同一设备连接。
04 Local / Broker 选择、资源键与粒度
| 设备/行为 | 推荐路径 | 授权资源粒度 | 物理串行粒度 |
|---|---|---|---|
| 独立单通道 TCP 电源,仅单 Worker 使用 | Local;若要跨进程维护则经原宿主代理或停机交接 | psu/PSU001/ch/1 + parent permit | PSU001/session/main |
| 8 通道电源,共同 SCPI/串口会话 | Broker | 每输出通道独占;整机 reset 取 parent exclusive | 整机 SessionKey |
| CAN/CAN-FD 卡,多个 DUT 共用接收 | Broker | bus permit + 发送规则;UDS 按 bus/address/session 排他 | SDK 线程安全域;UDS 会话队列 |
| RS485 多地址总线 | Broker | 站地址/功能;广播命令取 bus exclusive | 物理串口 SessionKey |
| 温箱、PLC 温度/联锁 | Broker 单一配方控制者 | chamber 控制权按 Run/group;各 DUT 仅订阅 | PLC/温箱真实连接 |
| 共享 DMM + 继电器矩阵 | Broker 原子任务 | DMM + Matrix 路由组合 | 切换→稳定→测量→释放片段 |
| 模拟设备 | Local 或仿真 Broker | 沿用真实资源键与策略 | 仿真注入队列与故障 |
4.1 三类键
ResourceKey 表示业务可授予的权利;PhysicalSessionKey 表示必须串行化的真实连接;AuthorityId 表示签发与校验租约的唯一主体。地址别名必须规范化为同一物理身份,例如串口号+设备序列号、IP/端口+仪器序列号、CAN 适配器序列号+通道;不能仅用工程师自定义 DeviceId 防重。
本版本 ResourceKey 统一小写、固定分隔符并使用 Ordinal 排序;配置保存时完成规范化。多通道 SessionGate 内必须把“选择通道 + 写入/查询 + 读取响应”视为不可拆分片段,避免两个穴位选错通道。
4.2 父子资源冲突
每个通道持有独占通道令牌及父设备的共享 Permit。整机 reset、断连、改通信参数、维护接线需要父设备独占,必须等待所有 Permit 归还。父 Permit 也进入租约集合和持久化表,不能仅锁一个字符串前缀。V3.0 的 DDL 给出排他叶资源基线;父共享 Permit 使用应用层在同一 BEGIN IMMEDIATE 内检查/更新的 parent_permit 表实现,不使用 SQLite 的行级锁假设。
跨 Authority 的多资源组采用协调式预留:Prepare(有期限、不可 I/O)→全部准备成功→Activate;任一步失败则 Abort。发生协调器崩溃时全部按预留期限失效进入检查。它不能宣称跨库原子事务;要求严格全有全无的动作必须将资源放入同一 Broker 域,或将整组动作作为一个 Broker Task。禁止持有域 A 的运行租约无限等待域 B。
05 工程师配置与发布工作流
沿用 DeviceConfigView、DeviceDebugView、DeviceMonitorView 分工。配置工程师编辑草稿,维护工程师申请维护会话,操作员启动生产。管理员发布权限不自动授予设备写权限。以下为配置工作台字段定义,不代表当前 UI 已实现。
| 配置页 | 关键字段 | 校验与反馈 |
|---|---|---|
| 设备注册 | DeviceId、Kind、DriverType/Version/Hash、Transport、EndpointRef、PhysicalIdentity、HostId | 驱动能力、位数、地址/物理身份去重;凭据只保存引用 |
| 执行位置 | ExecutionMode=Local/Broker、AuthorityId、BrokerId/OwnerProcessId、PhysicalSessionKey | 同一会话只能有一个宿主;BrokerId 与设备域一致 |
| 通道与资源 | ChannelId、Capabilities、ResourceKey、ParentKey、AccessPolicy、ConflictDomain | 通道唯一;整机动作与通道动作冲突展开完整 |
| 穴位绑定 | SlotId、Capability、DeviceId、ChannelId、GroupId、MappingVersion | 120 穴位能力覆盖,无缺失/重复功率通道;共享映射须显式标注 |
| Broker Task 模板 | TemplateId/Version、OpCode 白名单、周期、deadline、SafeStateProfile、订阅规则 | 禁止任意 C# / 脚本执行;周期能力与协议/看门狗匹配 |
| 仲裁与恢复 | TTL、RenewInterval、WaitTimeout、MaxTaskTime、HandoverTimeout、RecoveryProfile | Renew ≤ TTL/3;运行超时不能直接触发新 owner;安全配置需审核 |
| 发布预览 | 变更差异、受影响穴位/Run、Hash、验证报告、审批人 | 运行中影响连接/输出/映射的变更禁止直接 Apply |
5.1 批量生成 120 穴位
- 选择拓扑模板:“120×1 TCP”或“15×8 多通道”;设置地址规则、穴位范围和设备编号前缀。
- 生成草稿和映射预览,显示全部 120 条映射;单点覆盖保留来源与差异。
- 离线校验身份冲突、能力、限值、Broker 域和驱动依赖;通过后申请维护窗口执行连接探测、通道寻址和读回。
- 发布不可变 ConfigVersion;分发到各宿主 Prepare 并确认同一 Hash。
- 停止受影响 Run,撤销写权限,安全断连;各宿主 Apply 并自检。全部成功后才更新 Effective 指针;失败保持停用并按上一版配置恢复。
- 创建新 Run 时复制生效快照;既有 Run 继续引用原 Hash,不允许静默切版。
5.2 配置状态与失败处理
Draft → Validated → Published → Prepared → Effective → Retired。Published 内容不可修改,回滚也建立新的生效记录。多 Broker 生效采用停机窗口中的两阶段分发和最终确认;它不是跨机器数据库事务。若部分宿主 Apply 成功、部分失败,整组标记 ApplyFailed,阻止 Run 启动,直至所有宿主统一到旧版或新版并完成读回。
06 配置契约与启动校验
{
"schemaVersion": 3,
"configVersion": "CFG-20261103-001",
"topology": "15x8", "slotCount": 120,
"authorities": [
{"id":"broker-b01", "host":"IPC-01", "pipe":"aging.b01.v3"},
{"id":"local-ipc01", "host":"IPC-01", "db":"arb.db"}
],
"devices": [{
"deviceId":"PSU01", "executionMode":"Broker",
"authorityId":"broker-b01", "sessionKey":"psu01/main",
"physicalIdentity":"serial:TBD-PSU01",
"driver":{"type":"MultiChannelScpi", "version":"TBD", "sha256":"TBD"},
"endpointRef":"endpoint/psu01", "channels":8,
"accessPolicy":"ExclusiveChannelWithParentPermit",
"safeStateProfile":"PSU-SAFE-001"
}],
"bindings": [{"slotId":"S001", "capability":"Power",
"deviceId":"PSU01", "channelId":1, "resourceKey":"psu/psu01/ch/1"}],
"leasePolicy":{"ttlMs":30000,"renewMs":8000,"handoverMs":30000},
"taskPolicy":{"maxQueuedPerSession":128,"defaultDeadlineMs":5000},
"note":"字段示例,仅列一个设备和一个穴位;完整120映射见第17章。"
}JSON 采用固定序列化规则(UTF-8、稳定字段/数组排序、排除自身 Hash 字段)计算 SHA256。Secrets、运行状态、当前租约不得进入可迁移明文配置。Schema 校验和语义校验分别输出字段路径、错误码、影响穴位与建议处理。
启动顺序:读取有效版本 → 校验 manifest/包 Hash 与驱动位数 → 检查 Authority 唯一实例和 epoch → 打开数据库并迁移 → 加载设备身份与连接守卫 → 进入 Recovering → 执行无输出连接探测/状态读回 → 标记设备 Ready → 发布 HostReady。Run 启动时要求所有绑定资源 Ready,且宿主回报的配置 Hash 与 RunSnapshot 完全一致。示例中的 TBD 不允许通过生产发布门禁。
07 数据库边界、实体与一致性
| 库 | 唯一写入责任 | 主要内容 | 一致性边界 |
|---|---|---|---|
| aging_system.db / config.db | ConfigurationService / RunService | 不可变配置、Run 快照、版本索引、用户/审计、结果归档索引 | 配置发布与 Run 起始记录需事务;采样不混入配置库 |
| arb.db(同机 Local) | 受控 Local Lease Store;多进程短事务 | 排他资源状态、父 Permit、进程身份、租约历史 | 仅同一主机本地磁盘;PhysicalSessionKey Mutex 另行守卫 |
| broker-b01.db 等 | 各 Broker 单一写入器 | 本域 fence、任务日志、事件、结果 Outbox、恢复检查点 | 每个 Broker 一个 Authority;其他进程只走 IPC |
| 每 Run aging_data.db | ResultWriter | 样本、信号、步骤结果、质量和时间戳 | 批量写入,独立背压;通过 RunId / DUTExecutionId 关联 |
SQLite 使用本机磁盘;禁止放在 SMB/NAS 上让多机同时写 WAL。所有连接显式设置 foreign_keys、busy_timeout;WAL 只允许一个同时写事务,必须缩短事务并避免在事务内执行 I/O。启用 WAL 不等于任意写并发,也不等于无限吞吐。跨机扩展经 Broker 服务通信,不能共享同一个 arb.db 文件。[E1][E2]
关联关系:ConfigSnapshot 1:N Device / Resource / Binding / TaskTemplate;RunSnapshot 引用 Config Hash;ResourceState 1:N LeaseHistory;BrokerTask 引用 Config Hash 并在请求 JSON 中携带完整 grant 集;Task 1:N Event,Task 1:1 ResultOutbox。历史日志保留资源名和快照 Hash,不能被修改后的当前配置反向解释。
UTC 毫秒用于审计,宿主的单调时钟用于活跃租约和 deadline 判定。epoch + boot identity 防止重启后沿用旧单调时钟;本地系统时间跳变时停止续租/交接并进入恢复检查。SQLite datetime('now') 不是独立可信时钟。
08 SQLite DDL 与租约事务
以下两段 DDL 分别用于配置库和每个仲裁域的运行库。它们是新增 V3 schema,不直接对现有 data.db 执行 DROP/覆盖。迁移程序维护 schema_version、备份和回滚标记;生产迁移必须在停机窗口完成。
PRAGMA foreign_keys=ON;
PRAGMA journal_mode=WAL;
PRAGMA synchronous=FULL;
PRAGMA busy_timeout=3000;
CREATE TABLE config_snapshot (
config_hash TEXT PRIMARY KEY, version_id TEXT NOT NULL UNIQUE,
schema_version INTEGER NOT NULL CHECK(schema_version=3),
content_json TEXT NOT NULL, author TEXT NOT NULL,
published_utc_ms INTEGER NOT NULL
);
CREATE TABLE effective_config (
scope_id TEXT PRIMARY KEY, config_hash TEXT NOT NULL
REFERENCES config_snapshot(config_hash), revision INTEGER NOT NULL
);
CREATE TABLE resource_definition (
config_hash TEXT NOT NULL REFERENCES config_snapshot(config_hash),
resource_key TEXT NOT NULL, device_id TEXT NOT NULL, channel_id INTEGER NOT NULL,
parent_key TEXT, session_key TEXT NOT NULL, physical_identity TEXT NOT NULL,
execution_mode TEXT NOT NULL CHECK(execution_mode IN ('Local','Broker')),
authority_id TEXT NOT NULL, owner_process_id TEXT,
access_policy TEXT NOT NULL, safe_profile TEXT NOT NULL,
PRIMARY KEY(config_hash, resource_key)
);
CREATE TABLE slot_binding (
config_hash TEXT NOT NULL, slot_id TEXT NOT NULL, capability TEXT NOT NULL,
resource_key TEXT NOT NULL, group_id TEXT NOT NULL,
PRIMARY KEY(config_hash, slot_id, capability),
FOREIGN KEY(config_hash, resource_key)
REFERENCES resource_definition(config_hash, resource_key)
);
CREATE INDEX ix_binding_resource ON slot_binding(config_hash, resource_key);
CREATE TABLE broker_task_template (
config_hash TEXT NOT NULL REFERENCES config_snapshot(config_hash),
template_id TEXT NOT NULL, template_version TEXT NOT NULL,
operation_code TEXT NOT NULL, parameters_schema TEXT NOT NULL,
required_resources_json TEXT NOT NULL, max_duration_ms INTEGER NOT NULL,
safe_profile TEXT NOT NULL,
PRIMARY KEY(config_hash,template_id,template_version)
);
CREATE TABLE run_snapshot (
run_id TEXT PRIMARY KEY, config_hash TEXT NOT NULL
REFERENCES config_snapshot(config_hash), recipe_hash TEXT NOT NULL,
driver_manifest_hash TEXT NOT NULL, dbc_hash TEXT NOT NULL,
uds_hash TEXT NOT NULL, slot_set_json TEXT NOT NULL, started_utc_ms INTEGER NOT NULL
);
CREATE TABLE config_apply_ack (
apply_id TEXT NOT NULL, authority_id TEXT NOT NULL,
config_hash TEXT NOT NULL REFERENCES config_snapshot(config_hash),
phase TEXT NOT NULL, readback_json TEXT NOT NULL, utc_ms INTEGER NOT NULL,
PRIMARY KEY(apply_id,authority_id)
);PRAGMA foreign_keys=ON;
PRAGMA journal_mode=WAL;
PRAGMA synchronous=FULL;
PRAGMA busy_timeout=3000;
CREATE TABLE authority_meta (
authority_id TEXT PRIMARY KEY, epoch INTEGER NOT NULL,
boot_id TEXT NOT NULL, schema_version INTEGER NOT NULL
);
CREATE TABLE resource_state (
resource_key TEXT PRIMARY KEY, generation INTEGER NOT NULL DEFAULT 0,
phase TEXT NOT NULL CHECK(phase IN ('Free','Active','Draining','RecoveryRequired','Disabled')),
lease_id TEXT UNIQUE, owner_id TEXT, owner_type TEXT, group_id TEXT,
process_instance_id TEXT, epoch INTEGER NOT NULL,
expires_utc_ms INTEGER, handover_utc_ms INTEGER, updated_utc_ms INTEGER NOT NULL,
CHECK(phase <> 'Free' OR lease_id IS NULL)
);
CREATE INDEX ix_resource_expiry ON resource_state(phase,expires_utc_ms);
CREATE TABLE parent_permit (
parent_key TEXT NOT NULL, lease_id TEXT NOT NULL,
mode TEXT NOT NULL CHECK(mode IN ('Shared','Exclusive')),
owner_id TEXT NOT NULL, epoch INTEGER NOT NULL,
expires_utc_ms INTEGER NOT NULL,
PRIMARY KEY(parent_key,lease_id)
);
CREATE TABLE lease_history (
event_id INTEGER PRIMARY KEY AUTOINCREMENT, resource_key TEXT NOT NULL,
generation INTEGER NOT NULL, epoch INTEGER NOT NULL, lease_id TEXT,
owner_id TEXT, event_type TEXT NOT NULL, reason TEXT NOT NULL, utc_ms INTEGER NOT NULL
);
CREATE TABLE broker_task (
task_id TEXT PRIMARY KEY, client_instance_id TEXT NOT NULL,
idempotency_key TEXT NOT NULL, payload_sha256 TEXT NOT NULL,
config_hash TEXT NOT NULL, epoch INTEGER NOT NULL,
template_id TEXT NOT NULL, request_json TEXT NOT NULL,
state TEXT NOT NULL CHECK(state IN ('Accepted','Queued','Running','Succeeded',
'Failed','Cancelled','Expired','Unknown','RecoveryRequired')),
priority INTEGER NOT NULL, submitted_utc_ms INTEGER NOT NULL,
deadline_utc_ms INTEGER NOT NULL, started_utc_ms INTEGER, finished_utc_ms INTEGER,
result_json TEXT, error_code TEXT,
UNIQUE(client_instance_id,idempotency_key)
);
CREATE INDEX ix_task_queue ON broker_task(state,priority,submitted_utc_ms);
CREATE TABLE broker_event (
cursor INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT,
event_type TEXT NOT NULL, payload_json TEXT NOT NULL, utc_ms INTEGER NOT NULL
);
CREATE TABLE result_outbox (
result_id TEXT PRIMARY KEY, task_id TEXT NOT NULL UNIQUE REFERENCES broker_task(task_id),
payload_json TEXT NOT NULL, state TEXT NOT NULL CHECK(state IN ('Pending','Acked')),
attempt INTEGER NOT NULL DEFAULT 0, created_utc_ms INTEGER NOT NULL
);
CREATE TABLE recovery_checkpoint (
resource_key TEXT PRIMARY KEY, epoch INTEGER NOT NULL, status TEXT NOT NULL,
physical_readback_json TEXT NOT NULL, approved_by TEXT,
evidence_json TEXT NOT NULL, updated_utc_ms INTEGER NOT NULL
);
CREATE TABLE operation_audit (
audit_id INTEGER PRIMARY KEY AUTOINCREMENT, correlation_id TEXT NOT NULL,
principal_id TEXT NOT NULL, owner_id TEXT NOT NULL, task_id TEXT,
resource_key TEXT NOT NULL, action TEXT NOT NULL, outcome TEXT NOT NULL,
detail_json TEXT NOT NULL, utc_ms INTEGER NOT NULL
);-- 前提:请求资源已展开、规范化、去重、Ordinal排序,属于同一 Authority。
-- 每个叶资源必须 Free;Expired 不属于 Free。父 Permit 冲突也在本事务检查。
BEGIN IMMEDIATE;
-- 应用先 SELECT 并检查全部叶节点 + parent_permit,无跨域/缺项/冲突才继续。
SELECT resource_key, generation, phase, epoch FROM resource_state
WHERE resource_key IN (@r1, @r2);
-- 对每个叶资源执行下列参数化 UPDATE,并检查 changes() = 1。
UPDATE resource_state
SET generation=generation+1, phase='Active', lease_id=@lease_id,
owner_id=@owner_id, owner_type=@owner_type, group_id=@group_id,
process_instance_id=@process_id, epoch=@epoch,
expires_utc_ms=@expires, updated_utc_ms=@now
WHERE resource_key=@resource_key AND phase='Free' AND epoch=@epoch;
-- 父Permit冲突检查后插入;只要任一条失败,整个事务 ROLLBACK。
INSERT INTO parent_permit(parent_key,lease_id,mode,owner_id,epoch,expires_utc_ms)
VALUES(@parent,@permit_id,'Shared',@owner_id,@epoch,@expires);
-- 同事务插入 lease_history,COMMIT 成功后才返回 Grant。
COMMIT;
-- Renew:保持 Generation 不变;禁止过期租约复活。
UPDATE resource_state SET expires_utc_ms=@new_expires, updated_utc_ms=@now
WHERE resource_key=@resource_key AND lease_id=@lease_id
AND generation=@generation AND epoch=@epoch
AND phase='Active' AND expires_utc_ms > @now;
-- 父Permit同事务续期;组内任一项失败 => 回滚续期、组整体失效并隔离。
-- Draining 续期走专门分支,上限不得超过 handover_utc_ms。
-- TTL过期:保留Generation和owner证据,进入恢复,不直接清空或删行。
UPDATE resource_state SET phase='RecoveryRequired', updated_utc_ms=@now
WHERE phase IN ('Active','Draining') AND expires_utc_ms <= @now;
-- 只有恢复协调器读回合格、旧I/O已隔离、父Permit正确归还后可变为Free。
-- 正常Release亦须在SessionGate内完成排空及任务清理后再置Free。初始化时为已发布资源创建 Free 行,generation=0;删除设备也保留 fence 墓碑。生成号永不因释放、重启或清理历史回退。恢复旧数据库备份后必须更换 Authority identity/epoch,禁止旧 token 再次有效。SELECT 结果的分支与 changes() 检查由 C# 在同一连接事务内实现,不使用 SQLite 不支持的 SELECT INTO / FOR UPDATE。[E1]
持续数据、任务终态、Outbox 与 Event 在同一运行库事务提交;ACK 仅在消费者持久化成功后发送。Outbox 保留到 ACK,消费者按 ResultId 唯一约束去重。日志清理不得删除尚未 ACK、Unknown、RecoveryRequired 或被 Run 引用的证据。
09 C# 接口与兼容边界
以下为 V3.0 合约基线,可抽出为独立类库。使用 Task 和普通类以适配当前 net472,不依赖 TimeProvider、PeriodicTimer、record 或原生 await using。序列化工具应显式映射只读 LeaseGrant 构造器。Token 是授权引用,服务端还必须校验连接身份与 owner;不能把可见 token 当作签名凭据。
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Aging.ResourceContracts.V3
{
public enum ExecutionMode { Local, Broker }
public enum OwnerKind { Production, Maintenance, SystemSafety }
public enum TaskState { Accepted, Queued, Running, Succeeded, Failed,
Cancelled, Expired, Unknown, RecoveryRequired }
// Broker/Store签发;必须按持久化字段还原,不能在反序列化时生成新LeaseId。
public sealed class LeaseGrant
{
public string AuthorityId { get; }
public string ResourceKey { get; }
public string LeaseId { get; }
public string OwnerId { get; }
public long Epoch { get; }
public long Generation { get; }
public long ExpiresUtcMs { get; }
public LeaseGrant(string authorityId, string resourceKey, string leaseId,
string ownerId, long epoch, long generation, long expiresUtcMs)
{
AuthorityId=authorityId; ResourceKey=resourceKey; LeaseId=leaseId;
OwnerId=ownerId; Epoch=epoch; Generation=generation;
ExpiresUtcMs=expiresUtcMs;
}
}
public sealed class AcquireRequest
{
public string OwnerId { get; set; } = "";
public OwnerKind OwnerKind { get; set; }
public string ProcessInstanceId { get; set; } = "";
public string ConfigHash { get; set; } = "";
public string[] ResourceKeys { get; set; } = new string[0];
public int WaitTimeoutMs { get; set; }
}
public interface ILeaseScope
{
IReadOnlyList<LeaseGrant> Grants { get; }
CancellationToken LeaseLost { get; }
Task ReleaseAsync(CancellationToken cancellationToken);
}
public interface IResourceCoordinator
{
Task<ILeaseScope> AcquireGroupAsync(AcquireRequest request, CancellationToken ct);
Task<IReadOnlyList<LeaseGrant>> RenewGroupAsync(
IReadOnlyList<LeaseGrant> grants, CancellationToken ct);
Task RequestHandoverAsync(string resourceKey, string reason, CancellationToken ct);
}
public sealed class DeviceOperation
{
public string OperationCode { get; set; } = "";
public string ParametersJson { get; set; } = "{}";
public string IdempotencyKey { get; set; } = "";
public string ConfigHash { get; set; } = "";
public IReadOnlyList<LeaseGrant> Grants { get; set; } = new LeaseGrant[0];
public long DeadlineUtcMs { get; set; }
}
public sealed class OperationResult
{
public string RequestId { get; set; } = "";
public TaskState State { get; set; }
public string ErrorCode { get; set; } = "";
public string ReadbackJson { get; set; } = "{}";
public bool PhysicalStateKnown { get; set; }
}
public interface IDeviceExecutor
{
Task<OperationResult> ExecuteAsync(DeviceOperation op, CancellationToken ct);
}
public sealed class BrokerTaskRequest
{
public string ClientInstanceId { get; set; } = "";
public string TemplateId { get; set; } = "";
public string TemplateVersion { get; set; } = "";
public DeviceOperation Operation { get; set; } = new DeviceOperation();
}
public sealed class BrokerTaskTicket
{
public string TaskId { get; set; } = "";
public TaskState State { get; set; }
}
public interface IDeviceBrokerClient
{
Task<BrokerTaskTicket> SubmitAsync(BrokerTaskRequest request, CancellationToken ct);
Task<OperationResult> QueryAsync(string taskId, CancellationToken ct);
Task CancelAsync(string taskId, string reason, CancellationToken ct);
Task AckResultAsync(string resultId, CancellationToken ct);
}
}| 现有接口/类 | V3.0 迁移方式 | 影响 |
|---|---|---|
| IResourceManager | 保留旧单资源方法为过渡 Adapter;新增 IResourceCoordinator | 续期、组预约与异步状态查询为新接口,业务调用需适配 |
| ResourceOwnerToken | 仅保留业务 owner 元数据;I/O 使用每资源独立 LeaseGrant | 一个 owner 可持有多个不同 generation,禁止共用可变 Generation |
| IAutoDeviceFacade / IAutoDeviceLease | LeaseScope 注入心跳与 LeaseLost;显式异步 Release | Dispose 不得阻塞 UI;释放失败应可查询和进入恢复 |
| DeviceService | 通过 SnapshotResolver + ResourceRouter 调用 IDeviceExecutor | 已有 raw command 仅留受控维护入口,生产用类型化 OpCode |
| DriverFactory / DeviceManager | 区分宿主驱动与 BrokerProxy;Proxy 不加载设备 SDK | 位数冲突的 SDK 独立 Broker 进程,IPC 合约保持一致 |
| DebugSessionManager | 保存 session、grants、handover 与恢复结果 | 不能重新构造 owner token 代替恢复持久化 token;监控与写权限分开 |
10 执行入口、生命周期与取消语义
10.1 单次操作
- 步骤解析 Slot/Capability,得到冻结版本下的资源集合与宿主;检查 Run / DUTExecution 状态。
- 申请组预约(或复用已有 Scope),取得叶节点和父 Permit;启动独立续期任务。
- DeviceService 校验动作、参数、限值和软件联锁,构造 OperationCode 与 IdempotencyKey。
- LocalExecutor 或 Broker 在入队时校验;出队并持有 SessionGate 后再次校验完整 grant、epoch、ConfigHash、deadline 与连接身份。
- 执行不可拆分的选择通道/命令/响应片段;读回核对并记录质量。联锁故障打断可中断任务,已发动作按硬件能力处理。
- 提交 Task 终态、事件和 Outbox;结果回到步骤引擎,由引擎判定步骤/产品结果。
- Scope 退出先停止续期和后台任务,排空 I/O,再读回/安全交接并释放。失败进入 RecoveryRequired,绝不仅记录日志后假装可用。
10.2 伪代码:SessionGate 与有效期检查
// 执行算法示意,依赖的Store/Driver不是本文已实现代码。
using (await sessionGate.EnterAsync(sessionKey, ct))
{
await authority.ValidateAtDispatchAsync(op.Grants, principal, ct);
ValidateConfigAndDeadline(op);
await safety.CheckAsync(op, ct);
// 切换通道 + 写 + 读响应保持在同一Gate,禁止中途释放。
await journal.MarkRunningAsync(requestId, ct); // I/O之前持久化
var result = await driver.ExecuteAtomicAsync(op, ioToken);
await journal.CommitResultAndOutboxAsync(requestId, result, persistToken);
}
// 每个不可逆子动作前再次核对lease;续期不能抢同一被长任务占用的调度队列。
// 交接先关闭新入队、取消待执行任务、等待当前原子片段终止并读回;
// 未能确认终止时保持隔离,禁止仅因Generation增加就启用新owner。取消分三类:排队取消可确定未发送;执行中取消需驱动确认安全停止;响应等待取消仅停止等待,不能证明物理操作撤销。调用方的 CancellationToken 不得取消终态/审计持久化;终态保存使用有界的独立 token,失败则隔离。
不能在同一个非线程安全 SDK 会话中并发发送 SafeShutdown 与正常命令。安全通道必须有经验证的独立能力(如 PLC 硬线、独立控制口),否则等待会话退出,超时触发硬件策略并保持隔离。
11 Broker Task 模型、模板与状态机
Broker Task 是宿主执行的有界设备动作或可续租设备任务;它不是 C# Task 对象的序列化,也不是整个 DUT Recipe。Recipe 中的长 Delay 留在执行引擎;周期 CAN 发送、TesterPresent、采集订阅等可驻留 Broker,受 owner 租约和宿主看门狗约束。
| Task 类型 | 输入/资源 | 完成条件 | 失效策略 |
|---|---|---|---|
| OneShot / AtomicSequence | 类型化命令 + grant 集 + deadline | 写入及必要读回完成 | 超时按发送边界区分 Expired / Unknown |
| PeriodicTx | CAN 帧模板、bus、周期、持续期限、owner | Stop/到期/租约失效并确认停止 | 周期错失跳过过时帧;禁止重连后追补旧帧 |
| ProtocolSession | UDS 地址、会话参数、总期限、独占键 | 响应完成或显式退出 | TesterPresent 与业务动作统一会话调度;异常恢复协议状态 |
| Acquisition | 采样通道、周期、缓存策略、订阅 ID | 取消并确认停采/解绑 | 允许数据 Gap,质量标记 Stale/Disconnected;不可伪造连续数据 |
| SafeState | 受审安全模板及恢复授权 | 物理读回符合模板 | 失败维持 RecoveryRequired 并报警 |
11.1 任务字段与状态
TaskId、ClientInstanceId、IdempotencyKey、TemplateId/Version、PayloadSHA256、ConfigHash、RunId、DUTExecutionId、StepExecutionId、OwnerId、GrantSet、Priority、SubmittedAt、StartDeadline、MaxDuration、Attempt、State、Readback、ErrorCode、ResultId、CorrelationId。超时分为排队起始期限和运行上限,不能混用。
Accepted → Queued → Running → Succeeded / Failed / Cancelled / Unknown / RecoveryRequired;未启动任务可转 Expired,取消待执行任务可转 Cancelled。Accepted 必须已持久化,但不代表设备已执行。Running 落盘后才发 I/O;若崩溃在落盘与发送之间,恢复时仍按可能已发送处理,不能推测为未发送。
11.2 幂等与不可逆动作
同一 (ClientInstanceId, IdempotencyKey) 且同一 PayloadHash 返回已有 Task;同键不同内容报 IDEMPOTENCY_CONFLICT。客户端重连保留原 ClientInstanceId 查询,不生成新键重发。跨重启保留 Journal 与 epoch;旧 epoch 未开始任务全部失效,需新租约和新任务。命令执行与数据库提交之间无硬件事务,因此不承诺 exactly-once。SetVoltage 等动作能否重试必须按仪器行为和读回定义;触发、复位、烧写、计数动作默认禁止自动重放。
11.3 示例模板
{
"templateId":"psu.set-readback", "version":"1",
"allowedOperationCodes":["SelectChannel","SetVoltage","ReadVoltage"],
"resources":["psu/{device}/ch/{channel}","parent-permit:{device}"],
"atomicSession":"{device}/main", "maxDurationMs":3000,
"validation":{"voltage":"within channel and recipe limit"},
"onUnknown":"IsolateAndReadback", "retryClass":"ReconcileFirst"
}模板允许参数替换,不能指定任意驱动类型、地址、脚本或文件路径。模板变更参与配置 Hash。PeriodicTx 的周期/抖动验收必须由真实 SDK 和设备测量,Windows 普通 Task/Timer 不构成硬实时保证。
12 Broker 调度、队列与遥测
| 运行单元 | 调度规则 | 背压与限制 |
|---|---|---|
| IPC Accept / Auth | 协议握手、用户/进程身份验证、消息边界和长度限制 | 连接数、消息大小、调用频率限额;默认 1 MiB 消息上限,待压测 |
| Control Worker | Acquire/Renew/Release/Cancel/Query;与设备耗时 I/O 分离 | 保留控制带宽;限流防普通请求耗尽心跳处理 |
| Session Actor | 每真实连接单队列;每次仅一段原子 I/O | 队列上限示例128;满返回 QUEUE_FULL;过期任务不发送 |
| Task Scheduler | 安全事件优先、维护让渡协调、生产按 owner 轮转 | 同优先级公平;连续占用时间上限;不在任意命令中间抢占 |
| Protocol Worker | CAN 单接收循环 + 解码/订阅;UDS 分会话调度 | 按 SDK 线程安全能力分组;bus reset 属全局动作 |
| Journal / Outbox Writer | 单写入器批量提交;关键状态先持久化 | 不可丢写操作审计;磁盘满停止新增动作并隔离 |
| Telemetry Fanout | 单次采集多订阅;携带来源、质量、序列号、时间 | UI 最新值可覆盖;生产样本使用有界队列,Gap 必须记录 |
| Watchdog / Recovery | 独立心跳检查、异常隔离、恢复门禁 | 不能因为忙而无限续租;不得循环快速重启 |
Session Actor 释放会话锁后等待稳定延时还是保持锁,取决于设备耦合:共享 DMM/矩阵须保持资源权利及路由不变;仪器可独立定时输出的电源任务可释放操作锁,但保留通道预约。队列串行化本身不意味着允许多个 Run 同时改变同一温箱设定值,温箱控制权必须绑定兼容的 Run/group。
硬件 Safety 与普通调度独立。示例公平策略为每 owner 一次原子片段后轮转;优先级只决定下一片段,不绕过租约或联锁。持续采集/周期发送占用逻辑权利但不永久占用普通调度线程。定时线程数量由会话/SDK 能力确定,不能按 120 穴位创建 120 轮询线程。
13 IPC 协议、身份与事件恢复
同机基线使用 Windows Named Pipe;服务名含 Station/Broker/协议主版本。现有 PipeClient 必须先修复响应不可达、serverName、取消和等待问题,再作为候选基础;不能直接宣称现有 SendMessage 为可靠请求-响应。[E3]
{
"protocolVersion":3, "messageType":"SubmitTask", "requestId":"req-...",
"correlationId":"run-.../step-...", "clientInstanceId":"worker-01/boot-...",
"authorityId":"broker-b01", "expectedEpoch":42,
"configHash":"sha256:...", "deadlineUtcMs":1793673605000,
"body":{"templateId":"psu.set-readback", "templateVersion":"1",
"idempotencyKey":"run/dut/step/attempt-1", "grants":[], "parameters":{}}
}Body 这里只是外壳示例;真实设备 Submit 必须含完整 grant 集,空数组被拒绝。应答回传 RequestId、TaskId、BrokerEpoch、State、ErrorCode、RetryAdvice、ServerUtcMs;对不兼容协议拒绝连接,不自动降级安全字段。
帧协议选择“4 字节长度 + UTF-8 JSON”并实现精确读取、最大帧限制、半包、粘包、响应关联与取消;即便使用 Message 模式,也要处理 IsMessageComplete 和大消息。每条连接单写入泵、单读取分发器,多个请求通过 RequestId 关联,禁止多个调用方同时直接读同一 stream。
管道 ACL 限制指定服务身份和工程账户;服务端从 OS 身份映射 Principal,不相信 body 中的角色或 OwnerKind。OwnerId 必须绑定已认证的 Run/DebugSession/进程实例。跨机扩展采用 TLS 的独立服务端口、双向身份验证和网络白名单,Named Pipe 实现不会自动变成安全的跨机网关。
事件带持久化单调 cursor。客户端断线后先 Query 未终态 Task,再按最后 cursor 续订;若 cursor 已被保留策略清除,返回 CURSOR_GONE,客户端取全量任务/资源快照并重建订阅。进程 PID 加 ProcessStartTime/随机 ProcessInstanceId,避免 PID 复用被误认为原持有者。
14 资源仲裁与维护交接
| 当前状态 | 请求 | 决策与下一步 |
|---|---|---|
| Free 且 Ready | 生产/维护申请 | 同域原子授予; generation+1;发放组 grant |
| Production Active | 另一个生产请求同通道 | 有界排队或 RESOURCE_BUSY;不共享同一输出权 |
| Production Active | 维护申请 | 默认拒绝并显示 Run/穴位;授权让渡只在安全边界启动 Draining |
| Maintenance Active | 生产启动 | 拒绝启动,显示维护会话及影响范围 |
| 共享读订阅 | 维护写请求 | 先停止相关写权;被动读可保留,必要时标记 Stale |
| Draining | 现 owner 续期 | 仅续到 handover deadline;禁止新普通任务,允许收尾/安全动作 |
| Draining/Active 过期 | 新 owner 请求 | RecoveryRequired;等待排空、连接守卫与物理读回 |
| RecoveryRequired / Disabled | 任何普通写请求 | 拒绝;只有受控恢复动作允许执行 |
| 所有状态 | 硬件急停/严重联锁 | 硬件执行停机;软件阻断、记录状态并核查,不等待租约到期 |
14.1 预约、操作锁与读订阅
生产预约可覆盖一个 Run/步骤组,而 SessionGate 只覆盖一次原子 I/O。只读监控优先使用缓存;带副作用的 query、读清零寄存器、切换通道读取仍是排他 I/O,不能归为无锁读取。共享写只允许经明确的组合策略,例如 CAN 周期帧按 ID/周期规则分配,不能泛化为“同设备命令排队即可安全共享”。
14.2 完整维护交接
- 显示资源的占用者、Run、DUT、任务、剩余租约和父子影响;维护工程师填写理由。
- Run 协调器批准受影响穴位安全暂停,资源变 Draining;拒绝新写任务并停止周期任务。
- 等待在途片段结束;执行适用的 SafeStateProfile 并读回。超时转 RecoveryRequired,不自动给维护者。
- 原 owner 排空并释放;Local 还须确认旧句柄关闭和命名 Mutex 可获取。
- 确认独立联锁/断能手续后授予维护 Scope;维护动作全量审计。
- 退出维护先读回/复核、恢复通信配置和报警基线,再释放并由 RunService 明确恢复或新建 Run。
“Force release” 按钮不能直接 UPDATE/DELETE 租约行;它只能触发隔离及恢复流程。多人作业、拔线、接线和柜门操作的许可不由软件 token 单独判定。
15 Fencing、超时与 Local 边界
完整校验集合为 AuthorityId + Epoch + ResourceKey + LeaseId + Generation + OwnerId + ConfigHash + Principal,并检查活跃期、parent permit、deadline 与设备 Ready。generation 对每个资源单调增加;续期保持不变。Broker 在 SessionGate 内处理交接与最终发命令的序列,旧任务不能排队越过新 owner 的授权边界。
15.1 已发命令无法被 token 撤销
进程暂停可能发生在最后一次校验之后、写入之前;因此只有查询数据库再直连设备,不能提供强物理 fencing。Broker 在同一 I/O 域内以执行串行和交接排空避免旧/新 owner 并行;对已发出的命令,仍需等待完成、设备取消能力或独立断能。若驱动无法终止且状态未知,不授予新租约。
15.2 Local 的准入限制
Local 会话由配置固定到唯一 Worker,连接守卫使用规范化 PhysicalSessionKey 的同机命名 Mutex。获取守卫的专用宿主线程持续持有至连接关闭(Windows Mutex 为线程所有,不能简单跨 await 线程释放);驱动操作另用 SemaphoreSlim/SessionGate。线程异常与 AbandonedMutex 统一转恢复检查。Mutex 不因 TTL 过期释放,暂停的旧进程仍持有它,因此新进程不得接管。
远程客户端、另一个软件或同设备的其他别名不受此同机 Mutex 控制。若设备不能限制外部直连,且需要跨进程自动切换/共享,必须 Broker 化并配网络 ACL/访问凭据或硬件连接隔离;不能在文档中承诺绝对阻止所有外部命令。工程师维护 Local 设备时优先经现有宿主代理;若必须迁移连接,先停止旧 Worker 并确认无旧句柄,再走恢复门禁。
15.3 时间参数
建议初始 TTL=30 s、续期=8 s、让渡上限=30 s,均为设计默认值。权威宿主以单调时钟判定;UTC 仅用于持久化/展示,外来 UTC deadline 转换为有上限的本地预算。心跳失败后立即阻断新操作;没有有效确认不能无限延长本地有效期。系统重启或宿主 epoch 变化后全部旧 grant 失效,待核对后重新预约。
16 异常恢复与重启编排
| 故障 | 即时动作 | 恢复判据 / 禁止事项 |
|---|---|---|
| Worker 崩溃或断线 | 停其周期发送/订阅写任务;本域租约到期转 RecoveryRequired | 核对在途任务与输出;进程死亡不等于设备输出关闭 |
| Broker 崩溃 | 受影响 DUT 标记设备不可用;Supervisor 限频拉起 | 持久化 epoch+1;Running→Unknown;旧队列失效;读回后 Ready,禁止重放旧命令 |
| 宿主暂停/卡死 | 心跳超时、联锁策略介入;新 owner 不抢占 | 确认旧 I/O 宿主退出或隔离、硬件状态已知 |
| TCP/串口断线、USB重枚举 | Session Faulted;不缓存并追发旧写命令 | 重新识别设备序列号、通道/配置、输出状态;更换设备需配置审批 |
| 超时/回包丢失 | 区分未发送与可能已执行;后者 Unknown | 按模板读回;无法判定则人工恢复,不按 RetryAdvice 盲发 |
| DB busy / disk full / 损坏 | 限制短重试;停止接受新写动作;关键审计失败即隔离 | DB 完整性检查、空间恢复、Outbox 补投;不能绕开 Journal 继续生产 |
| 系统断电/重启 | 由硬件保障断能策略;启动进入 Recovering | 旧 epoch失效;核对SN/穴位、物理输出和温箱;创建恢复记录 |
| 配置Hash不一致 | 拒绝启动和操作 | 对齐宿主版本、驱动包与映射,重新 Prepare/Apply |
| CAN bus-off/UDS会话失效 | 隔离总线/会话;停止相关周期任务,标记数据质量 | 有界重连、地址/会话重新建立;不能把协议会话当成连续有效 |
| 安全联锁动作 | 硬件执行;软件停止新任务并记录原因/受影响资源 | 人工复位并读回,生产恢复必须显式批准 |
| 结果消费者断线 | Outbox 保留,限制积压并报警 | 按ResultId补投,消费者落盘再ACK;不能吞掉失败结果 |
16.1 Broker 重启步骤
- 获取 BrokerId 单实例守卫;同一物理会话不允许双实例同时启动。
- 数据库短事务增加 epoch 并记录 boot identity;旧 Active/Draining→RecoveryRequired,旧 Running→Unknown,未开始旧任务→Expired。
- 阻断普通写请求,加载有效配置,验证设备身份,建立连接;读取输出/通道/协议状态。
- 按设备安全模板执行经审核的恢复动作。安全状态不是统一“所有输出 Off”:温箱、冷却与被测件有各自安全要求。
- 恢复检查点和证据持久化;若需人工审批则等待。全部通过后资源 Free/Ready,再通知 RunService。
- RunService 对每个 DUT 记录中断窗口与数据 Gap,决定继续、重新执行可重入步骤或终止重测。168 h 等长时老化如何计时须按产品策略冻结。
Supervisor 建议退避 1/2/5/10/30 s,5 分钟内重启超过 3 次锁定报警,数值为待验证默认。软件重启恢复仅表示通信和状态恢复,不表示测试结果自动合格。
17 120 穴位部署方案与完整映射
基线选择单 IPC、15 台 8 通道 PSU、120 个输出通道。建议划分 3 个设备域,每域 40 穴位、5 台 PSU;6 个 Worker,每 Worker 20 个 DUTContext。一个 HMI、一个 ResultWriter 和一个 Supervisor 构成运维基线。Worker 与 Broker 数量由进程隔离和压测决定,不是硬件既定事实;CAN 卡/PLC/温箱按物理 SDK 与控制域分配,不能仅因 PSU 分成 3 域就复制连接。
| 域 | Worker / 穴位 | PSU | Broker职责 |
|---|---|---|---|
| B01 | W01:S001–S020;W02:S021–S040 | PSU01–PSU05;各8通道 | PSU共享会话;本域资源与任务 |
| B02 | W03:S041–S060;W04:S061–S080 | PSU06–PSU10;各8通道 | 同上,独立数据库和故障域 |
| B03 | W05:S081–S100;W06:S101–S120 | PSU11–PSU15;各8通道 | 同上,独立数据库和故障域 |
| 全局共享 | 所有涉及同一温箱/PLC的Run | 温箱01/PLC01/共用CAN(实际TBD) | 固定唯一宿主;必要时增加B00;控制权按Run/group分配 |
17.1 映射公式与边界
对穴位 s∈[1,120]:PSU=⌊(s−1)/8⌋+1;CH=((s−1) mod 8)+1;Broker=⌊(PSU−1)/5⌋+1;Worker=⌊(s−1)/20⌋+1。例如 S001→PSU01/CH1,S008→PSU01/CH8,S009→PSU02/CH1,S120→PSU15/CH8。
W01/W02 等的 20 穴位边界会切分一台多通道 PSU(如 PSU03);这是选择 Broker 的实际原因之一。通道预约独立,整机命令等待父 Permit,SessionGate 保证选通道和响应不串线。以下展开全部 120 条输出映射,通信地址、序列号和通道额定值须由工程师填充后才可发布。
120 穴位映射表(完整)
| 穴位 | Worker | Broker | 设备 | 通道 | ResourceKey |
|---|---|---|---|---|---|
| S001 | W01 | B01 | PSU01 | 1 | psu/psu01/ch/1 |
| S002 | W01 | B01 | PSU01 | 2 | psu/psu01/ch/2 |
| S003 | W01 | B01 | PSU01 | 3 | psu/psu01/ch/3 |
| S004 | W01 | B01 | PSU01 | 4 | psu/psu01/ch/4 |
| S005 | W01 | B01 | PSU01 | 5 | psu/psu01/ch/5 |
| S006 | W01 | B01 | PSU01 | 6 | psu/psu01/ch/6 |
| S007 | W01 | B01 | PSU01 | 7 | psu/psu01/ch/7 |
| S008 | W01 | B01 | PSU01 | 8 | psu/psu01/ch/8 |
| S009 | W01 | B01 | PSU02 | 1 | psu/psu02/ch/1 |
| S010 | W01 | B01 | PSU02 | 2 | psu/psu02/ch/2 |
| S011 | W01 | B01 | PSU02 | 3 | psu/psu02/ch/3 |
| S012 | W01 | B01 | PSU02 | 4 | psu/psu02/ch/4 |
| S013 | W01 | B01 | PSU02 | 5 | psu/psu02/ch/5 |
| S014 | W01 | B01 | PSU02 | 6 | psu/psu02/ch/6 |
| S015 | W01 | B01 | PSU02 | 7 | psu/psu02/ch/7 |
| S016 | W01 | B01 | PSU02 | 8 | psu/psu02/ch/8 |
| S017 | W01 | B01 | PSU03 | 1 | psu/psu03/ch/1 |
| S018 | W01 | B01 | PSU03 | 2 | psu/psu03/ch/2 |
| S019 | W01 | B01 | PSU03 | 3 | psu/psu03/ch/3 |
| S020 | W01 | B01 | PSU03 | 4 | psu/psu03/ch/4 |
| S021 | W02 | B01 | PSU03 | 5 | psu/psu03/ch/5 |
| S022 | W02 | B01 | PSU03 | 6 | psu/psu03/ch/6 |
| S023 | W02 | B01 | PSU03 | 7 | psu/psu03/ch/7 |
| S024 | W02 | B01 | PSU03 | 8 | psu/psu03/ch/8 |
| S025 | W02 | B01 | PSU04 | 1 | psu/psu04/ch/1 |
| S026 | W02 | B01 | PSU04 | 2 | psu/psu04/ch/2 |
| S027 | W02 | B01 | PSU04 | 3 | psu/psu04/ch/3 |
| S028 | W02 | B01 | PSU04 | 4 | psu/psu04/ch/4 |
| S029 | W02 | B01 | PSU04 | 5 | psu/psu04/ch/5 |
| S030 | W02 | B01 | PSU04 | 6 | psu/psu04/ch/6 |
| S031 | W02 | B01 | PSU04 | 7 | psu/psu04/ch/7 |
| S032 | W02 | B01 | PSU04 | 8 | psu/psu04/ch/8 |
| S033 | W02 | B01 | PSU05 | 1 | psu/psu05/ch/1 |
| S034 | W02 | B01 | PSU05 | 2 | psu/psu05/ch/2 |
| S035 | W02 | B01 | PSU05 | 3 | psu/psu05/ch/3 |
| S036 | W02 | B01 | PSU05 | 4 | psu/psu05/ch/4 |
| S037 | W02 | B01 | PSU05 | 5 | psu/psu05/ch/5 |
| S038 | W02 | B01 | PSU05 | 6 | psu/psu05/ch/6 |
| S039 | W02 | B01 | PSU05 | 7 | psu/psu05/ch/7 |
| S040 | W02 | B01 | PSU05 | 8 | psu/psu05/ch/8 |
| S041 | W03 | B02 | PSU06 | 1 | psu/psu06/ch/1 |
| S042 | W03 | B02 | PSU06 | 2 | psu/psu06/ch/2 |
| S043 | W03 | B02 | PSU06 | 3 | psu/psu06/ch/3 |
| S044 | W03 | B02 | PSU06 | 4 | psu/psu06/ch/4 |
| S045 | W03 | B02 | PSU06 | 5 | psu/psu06/ch/5 |
| S046 | W03 | B02 | PSU06 | 6 | psu/psu06/ch/6 |
| S047 | W03 | B02 | PSU06 | 7 | psu/psu06/ch/7 |
| S048 | W03 | B02 | PSU06 | 8 | psu/psu06/ch/8 |
| S049 | W03 | B02 | PSU07 | 1 | psu/psu07/ch/1 |
| S050 | W03 | B02 | PSU07 | 2 | psu/psu07/ch/2 |
| S051 | W03 | B02 | PSU07 | 3 | psu/psu07/ch/3 |
| S052 | W03 | B02 | PSU07 | 4 | psu/psu07/ch/4 |
| S053 | W03 | B02 | PSU07 | 5 | psu/psu07/ch/5 |
| S054 | W03 | B02 | PSU07 | 6 | psu/psu07/ch/6 |
| S055 | W03 | B02 | PSU07 | 7 | psu/psu07/ch/7 |
| S056 | W03 | B02 | PSU07 | 8 | psu/psu07/ch/8 |
| S057 | W03 | B02 | PSU08 | 1 | psu/psu08/ch/1 |
| S058 | W03 | B02 | PSU08 | 2 | psu/psu08/ch/2 |
| S059 | W03 | B02 | PSU08 | 3 | psu/psu08/ch/3 |
| S060 | W03 | B02 | PSU08 | 4 | psu/psu08/ch/4 |
| S061 | W04 | B02 | PSU08 | 5 | psu/psu08/ch/5 |
| S062 | W04 | B02 | PSU08 | 6 | psu/psu08/ch/6 |
| S063 | W04 | B02 | PSU08 | 7 | psu/psu08/ch/7 |
| S064 | W04 | B02 | PSU08 | 8 | psu/psu08/ch/8 |
| S065 | W04 | B02 | PSU09 | 1 | psu/psu09/ch/1 |
| S066 | W04 | B02 | PSU09 | 2 | psu/psu09/ch/2 |
| S067 | W04 | B02 | PSU09 | 3 | psu/psu09/ch/3 |
| S068 | W04 | B02 | PSU09 | 4 | psu/psu09/ch/4 |
| S069 | W04 | B02 | PSU09 | 5 | psu/psu09/ch/5 |
| S070 | W04 | B02 | PSU09 | 6 | psu/psu09/ch/6 |
| S071 | W04 | B02 | PSU09 | 7 | psu/psu09/ch/7 |
| S072 | W04 | B02 | PSU09 | 8 | psu/psu09/ch/8 |
| S073 | W04 | B02 | PSU10 | 1 | psu/psu10/ch/1 |
| S074 | W04 | B02 | PSU10 | 2 | psu/psu10/ch/2 |
| S075 | W04 | B02 | PSU10 | 3 | psu/psu10/ch/3 |
| S076 | W04 | B02 | PSU10 | 4 | psu/psu10/ch/4 |
| S077 | W04 | B02 | PSU10 | 5 | psu/psu10/ch/5 |
| S078 | W04 | B02 | PSU10 | 6 | psu/psu10/ch/6 |
| S079 | W04 | B02 | PSU10 | 7 | psu/psu10/ch/7 |
| S080 | W04 | B02 | PSU10 | 8 | psu/psu10/ch/8 |
| S081 | W05 | B03 | PSU11 | 1 | psu/psu11/ch/1 |
| S082 | W05 | B03 | PSU11 | 2 | psu/psu11/ch/2 |
| S083 | W05 | B03 | PSU11 | 3 | psu/psu11/ch/3 |
| S084 | W05 | B03 | PSU11 | 4 | psu/psu11/ch/4 |
| S085 | W05 | B03 | PSU11 | 5 | psu/psu11/ch/5 |
| S086 | W05 | B03 | PSU11 | 6 | psu/psu11/ch/6 |
| S087 | W05 | B03 | PSU11 | 7 | psu/psu11/ch/7 |
| S088 | W05 | B03 | PSU11 | 8 | psu/psu11/ch/8 |
| S089 | W05 | B03 | PSU12 | 1 | psu/psu12/ch/1 |
| S090 | W05 | B03 | PSU12 | 2 | psu/psu12/ch/2 |
| S091 | W05 | B03 | PSU12 | 3 | psu/psu12/ch/3 |
| S092 | W05 | B03 | PSU12 | 4 | psu/psu12/ch/4 |
| S093 | W05 | B03 | PSU12 | 5 | psu/psu12/ch/5 |
| S094 | W05 | B03 | PSU12 | 6 | psu/psu12/ch/6 |
| S095 | W05 | B03 | PSU12 | 7 | psu/psu12/ch/7 |
| S096 | W05 | B03 | PSU12 | 8 | psu/psu12/ch/8 |
| S097 | W05 | B03 | PSU13 | 1 | psu/psu13/ch/1 |
| S098 | W05 | B03 | PSU13 | 2 | psu/psu13/ch/2 |
| S099 | W05 | B03 | PSU13 | 3 | psu/psu13/ch/3 |
| S100 | W05 | B03 | PSU13 | 4 | psu/psu13/ch/4 |
| S101 | W06 | B03 | PSU13 | 5 | psu/psu13/ch/5 |
| S102 | W06 | B03 | PSU13 | 6 | psu/psu13/ch/6 |
| S103 | W06 | B03 | PSU13 | 7 | psu/psu13/ch/7 |
| S104 | W06 | B03 | PSU13 | 8 | psu/psu13/ch/8 |
| S105 | W06 | B03 | PSU14 | 1 | psu/psu14/ch/1 |
| S106 | W06 | B03 | PSU14 | 2 | psu/psu14/ch/2 |
| S107 | W06 | B03 | PSU14 | 3 | psu/psu14/ch/3 |
| S108 | W06 | B03 | PSU14 | 4 | psu/psu14/ch/4 |
| S109 | W06 | B03 | PSU14 | 5 | psu/psu14/ch/5 |
| S110 | W06 | B03 | PSU14 | 6 | psu/psu14/ch/6 |
| S111 | W06 | B03 | PSU14 | 7 | psu/psu14/ch/7 |
| S112 | W06 | B03 | PSU14 | 8 | psu/psu14/ch/8 |
| S113 | W06 | B03 | PSU15 | 1 | psu/psu15/ch/1 |
| S114 | W06 | B03 | PSU15 | 2 | psu/psu15/ch/2 |
| S115 | W06 | B03 | PSU15 | 3 | psu/psu15/ch/3 |
| S116 | W06 | B03 | PSU15 | 4 | psu/psu15/ch/4 |
| S117 | W06 | B03 | PSU15 | 5 | psu/psu15/ch/5 |
| S118 | W06 | B03 | PSU15 | 6 | psu/psu15/ch/6 |
| S119 | W06 | B03 | PSU15 | 7 | psu/psu15/ch/7 |
| S120 | W06 | B03 | PSU15 | 8 | psu/psu15/ch/8 |
17.2 120×1 Local 替代方案
若实物为 120 台独立 TCP PSU,按 6 Worker×20 会话固定归属;每 Worker 持 20 个设备连接,但使用异步收发与有界并发,不建 20 条专用轮询线程。共享 CAN/PLC/温箱仍经 Broker;Local PSU 维护经所属 Worker 代理或停机交接。若仪器全局命令跨穴位、SDK 不允许多实例或需自动宿主切换,应转 Broker。两种拓扑由 ConfigVersion 明确选择,不能混用映射公式。
17.3 多机扩展
可将每 40 穴位域迁移到独立 IPC,通过 TLS Broker API 连接;数据库仍保存在各自主机。共享温箱只设一个控制宿主,断网策略由独立联锁执行。域迁移需停用、隔离旧宿主、更新 Authority/epoch、应用新配置并核对输出,不做在线双主切换。
18 容量估算、数据与性能预算
以下数字用于建立压测负载,均非现有软件或硬件实测。独立电压/电流样本与 CAN 信号数据需分别预算,不能把 UI 刷新频率当作采样频率。
| 项目 | 计算/建议起点 | 验证方式 |
|---|---|---|
| PSU采样,10 Hz/穴位 | 120×10=1200 条组合V/I记录/s;拆两条则2400条/s | 驱动支持的批量读、单机/整柜测量;协议能力未确认 |
| 10 Hz持续168 h | 1200×604800=725,760,000 条记录 | 先确认是否必须全量长期存储;按原始与汇总分层 |
| 存储模型,100 B/组合样本 | 约120,000 B/s、10.368 GB/天、72.576 GB/168 h(十进制) | 未计索引/WAL/文件系统;预留系数≥2的起点,实测后定盘 |
| 单Broker,40穴位 | 示例400组合样本/s;共享协议会话可能低于此能力 | 对每台8通道PSU测全通道读回耗时 |
| 单PSU利用率 | ρ=Σ(命令到达率×服务时间);建议持续ρ≤0.6–0.7起测 | 80次通道读/s×10 ms=0.8,已超建议余量;需批量或降采样 |
| Broker排队预算 | start deadline示例5 s;AtomicSequence示例3 s | 分别统计Queue/Driver/DB/IPC延迟,不能将TTL作为排队预算 |
| UI刷新 | 聚合2–5 Hz,采样独立;只呈现最新值与质量 | 120穴位、报警风暴、窗口切换,观察帧阻塞和内存 |
| 续期负载 | 120个通道若每8s续一次≈15行/s;应按组事务聚合 | 与Task Journal及采样库隔离;父Permit和事件另计 |
| CAN预算 | Σ帧率×实际位数/有效总线速率,含仲裁、填充、重发和UDS | 型号、位速率、FD数据段与总线数TBD;不能凭120穴位直接承诺 |
建议按小时/日分片或批量列式文件归档,不让单个 7 亿行 SQLite 表无限增长;保持 Run 索引与分片 Hash 可追溯。原始 CAN 帧与解码信号按追溯等级选择保留,质量事件与故障前后窗口优先。采样写入背压达到上限时按 Recipe 策略暂停/报警;不得静默丢样后仍判定数据完整。
性能门禁建议:控制心跳处理 p99<1 s、普通 IPC 本机空载往返 p95<20 ms、120穴位 UI 无长时间冻结;这些均为目标起点,必须列出 CPU/内存/磁盘/驱动/负载条件实测。周期帧和 UDS 时序用硬件时间戳验证。
19 安装、观测与运维
19.1 打包与目录
C:\Program Files\AgingPlatform\ # 只读应用/签名驱动包
Hmi\ Worker\ Broker\ Supervisor\
C:\ProgramData\AgingPlatform\Station01\
config\{hash}\manifest.json # 不可变发布快照
system\aging_system.db
local\arb.db
broker\B01\broker-b01.db
broker\B02\broker-b02.db
broker\B03\broker-b03.db
runs\{RunId}\... # 数据分片/报告索引
logs\ recovery\ backups\生产数据使用服务账户可写的 ProgramData 或独立数据盘,不把原型的 %LOCALAPPDATA%\PluginIntegrationTest 作为正式多账户共享路径。所有插件从统一配置服务取得路径;禁止各插件自行拼接不同 BaseFolder。
上线前冻结 x86/x64、SDK版本、.NET依赖和包 Hash。Broker 可采用 Windows Service;若 SDK 需要交互桌面,则使用受控用户进程并验证服务会话差异。驱动隔离范围由 SDK 全局状态决定,不能将一张卡的全局句柄分到多个 Broker。
19.2 观测与告警
统一 CorrelationId=Run/DUT/Step/Task;日志记录 BrokerId、epoch、ResourceKey、SessionKey、LeaseId 摘要、generation、ConfigHash、QueueWait、IoLatency、RetryCount、ReadbackQuality。运行指标包括活跃/隔离资源数、心跳余量、队列深度、Unknown任务、Outbox积压、数据Gap、DB busy时间、磁盘剩余、重启次数与CAN bus-off。
告警必须带影响穴位和处理入口,区分硬件联锁、通信故障、仲裁冲突和维护中;不能将维护占用持续刷成设备故障。诊断导出包含版本、匿名化日志、任务状态和恢复证据,排除凭据。备份使用 SQLite 一致性备份方式,不能仅复制主 .db 而忽略活动 WAL。
19.3 升级与回退
停止新Run → 排空任务/撤销写权 → 确认安全态 → 一致性备份 → 数据迁移 → 升级宿主及客户端 → 协议握手/自检 → 配置Hash一致 → 恢复生产。若 schema 不向后兼容,不能直接回退旧二进制;使用验证过的备份恢复并提升 epoch,保留升级期间日志及结果。
20 典型运行场景与方案原理
以下用实际调用过程说明各层为什么存在。场景中的编号、时间和电压均为解释示例,不是设备的已确认工艺参数。
20.1 两个 Worker 操作同一台多通道电源
W01 的 S020 与 W02 的 S021 分别映射到 PSU03/CH4、PSU03/CH5。两者各持有本通道的排他租约和 PSU03 的父共享 Permit,因此可同时处于“有权使用”状态。两条操作均交给 B01;B01 的 PSU03 SessionGate 先完整执行 CH4 的“选择通道→设定→读回”,再执行 CH5。通道授权并行、物理会话串行,因此无需反复打开连接,也不会把 CH4 的设定写到 CH5。
若有人要执行 PSU03 整机 Reset,它要求父独占;CH4 或 CH5 任一 Permit 仍在使用时都不能执行。这样既保留通道独立性,又覆盖整机命令的影响范围。
20.2 独立 TCP 电源走 Local
PSU024 固定归属于 W02。Recipe 通过 S024/Power 找到 LocalExecutor;W02 持有连接守卫、通道租约和会话队列,直接调用驱动。HMI 监控只读取 W02 发布的状态缓存。维护请求经 W02 的受控入口完成;若要换宿主,必须先停止 W02 的旧连接并通过恢复检查。Local 的价值是减少代理跳转和独立进程数量,代价是不能仅凭租约过期进行跨进程自动接管。
20.3 维护工程师要操作正在生产的通道
HMI 显示该通道由 Run-A/S024 使用。申请维护不会立即得到写权限:RunService 先让受影响 DUT 到达安全边界,资源转为 Draining;宿主停止接收新普通写操作,排空当前片段,执行适用安全动作并读回。确认旧连接/任务已隔离后,旧租约释放,新的维护 Grant 才能签发。交接超时会停在 RecoveryRequired;“等了30秒”本身不能成为接管依据。
20.4 Broker 已发命令,但客户端没收到结果
客户端使用同一 IdempotencyKey 查询或重提。若 Journal 已有终态,返回原结果;若只记录 Running,且无法判断硬件是否执行,则返回 Unknown 并核对读回,不能生成新请求再触发一次。这说明 Task Journal 解决“请求是否见过、结果是否保存”,而不是替硬件实现数据库事务。
20.5 Broker 重启后如何恢复
B01 重启先提升 epoch,旧 Grant 和旧排队任务失效,PSU01–05 暂时不可写。宿主重新识别设备、读取输出和协议状态;检查合格后资源回到 Free/Ready。W01/W02 必须重新申请租约,RunService 根据中断窗口决定继续还是重测。B02/B03 的独立 PSU 域可继续运行;若故障涉及共用温箱/PLC,则按真实共享影响范围处理,不能假设只有40穴位受影响。
20.6 120 个 DUT 等待与单次总线采集
120 个 DUTContext 各自保存步骤、SN、计时和结果,但等待使用异步计时,不对应120条轮询线程。同一 CAN 总线只由一个接收器读取,再解码并向多个 DUT 分发。Broker 的物理采集负责“数据从哪里来、是否新鲜”,Recipe 引擎负责“这个 DUT 是否达到工艺条件”,从而避免120次读取同一总线。
21 方案适用条件与限制
方案是否成立取决于物理拓扑和设备协议。以下条件直接决定 Local/Broker 路由、资源粒度及恢复行为;它们是方案边界,不是研发排期。
| 条件 | 对方案的影响 | 未明确时的处理 |
|---|---|---|
| PSU 为120×1还是15×8 | 前者可固定Local归属;后者跨Worker共享会话宜Broker | 保留两种拓扑,禁止混用映射公式 |
| 设备是否允许多个会话、全局命令影响哪些通道 | 决定PhysicalSessionKey、父子资源和SessionGate范围 | 按更大的冲突域保守隔离 |
| CAN/SDK是否单实例、线程安全域与UDS地址 | 决定一个Broker能持有什么、哪些任务可并行 | 不拆分同一全局SDK句柄;地址冲突拒绝发布 |
| 温箱/PLC安全态与人员作业许可 | 决定谁可控制设定、交接读回与独立断能动作 | 不以软件TTL或令牌代替硬件安全条件 |
| 动作能否取消、是否幂等、能否可靠读回 | 决定超时是可重试还是Unknown/恢复检查 | 不可逆动作默认不自动重放 |
| 采样率、保留周期和总线实际能力 | 决定队列、批量采集、数据分片与磁盘容量 | 将容量数字视作负载假设,实测后配置 |
| Recipe允许的暂停、恢复与计时规则 | 决定掉线后续跑、步骤重入还是整件重测 | 保持中断证据,不自动判定合格 |
| 多机共享与第三方直连 | 决定是否必须Broker、TLS和网络访问控制 | 禁止共享SMB上的arb.db,禁止绕过唯一宿主 |
| 6Worker/3Broker是否符合真实SDK故障域 | 决定进程分组能否达到预期隔离效果 | 进程数作为建议;共享硬件按真实影响范围处理 |
本文可以说明仲裁和恢复规则,但不能单独证明设备已达到节拍、硬实时、物理安全或168 h长稳要求。默认时间、队列容量和采样率均是可配置的初始假设;工程师发布配置时应填入实际设备参数。
22 来源、交付范围与阅读说明
本 HTML 所有样式、脚本与架构图均内置,打开与打印不依赖网络。全文含 22 章、3 张可放大架构/状态/部署图、完整 120 穴位映射、C# 合约、SQLite DDL、故障恢复矩阵和典型运行场景。附带原生 Draw.io 文件供工程编辑;HTML 内也可下载同一原生图及 SQL / C# 合约。
绘图规则:C:\Users\xcw11.HOMEAI\.codex\skills\drawio-default-style\SKILL.md,版本 1.0.0;同时遵循 Draw.io 插件原生格式/导出规则。图示内容为目标设计,静态实现事实以第 2 章为准。图 2 的安全确认包括在途命令终止、连接隔离、设备读回与必要的硬件作业许可。
参考技术资料(2026-11-03核对):[E1] SQLite Transaction:写事务与 BEGIN IMMEDIATE;[E2] SQLite WAL:单机约束与读写并发边界;[E3] Microsoft Named Pipes:进程间管道与身份能力。本文 IPC 帧、仲裁、队列、恢复流程是本项目设计选择,并非这些文档提供的完整方案。
| 来源 | 路径 | 用途 | SHA256(本次读取) |
|---|---|---|---|
| S1 | D:\TestPlatform\Aging_Software_Design_V2.1 (11).docx | V2.1设计基线:Run/DUT、120穴位、仪器管理与数据分层 | 36306c77933800fa0a9bd9719a77178021b44162d5034d244c8e3405d78238db |
| S2 | D:\TestPlatform\TapProject\docs\MultiProcess_Instrument_Resource_Solution.html | 旧多进程方案;本次对安全/IPC结论作修订 | b8e1ec02155baca47e61fcdeaf4e36c0086c73fa658a7954ee92801037bb12ac |
| S3 | D:\TestPlatform\TapProject\docs\DeviceConfig_DeviceDebug_Architecture.html | 配置、维护会话与调试审计设计 | c64f342e88828913b75c8813b69051028b5670e27e028ebb78e6bc732cdb80c4 |
| S4 | D:\TestPlatform\TapProject\Aging.Infrastructure\Core\ResourceManager.cs | 当前进程内预约/代号/操作租约实现 | 61e75bb59c690e8bd5ff1d2b6672bdaa72693dbf27c324adad03d23ed03f8319 |
| S5 | D:\TestPlatform\TapProject\Aging.Infrastructure\Contracts\ResourceOwnerToken.cs | OwnerToken当前字段和构造行为 | c45b12b4843019b6ddca7c267d8fec962971fd9fdd35903ff5f1b8a08e6ad09d |
| S6 | D:\TestPlatform\TapProject\Aging.Infrastructure\Services\DeviceService.cs | 命令校验及驱动调用路径 | 72a95fe5ddd07e5336b5a9b0e00c34d7ecf9abf104edd511956218bae69a1357 |
| S7 | D:\TestPlatform\TapProject\Aging.Infrastructure\Services\AutoDeviceFacade.cs | 预约作用域及释放错误处理 | 67b7521a593b5961bf70bf998c72806e8e62d63afb41969d2058c7ec522e8e19 |
| S8 | D:\TestPlatform\TapProject\ConnectionUtil\PipeClient.cs | IPC响应不可达、serverName行为 | b17451398fdffd7216c7b4c4db55f541cee76175b66ae93d822964bf59311870 |
| S9 | D:\TestPlatform\TapProject\ConnectionUtil\PipeServer.cs | 现有命名管道服务端基础 | 58a3fc4f6f6ef8fe69a2781e429a557f0d529104717eb90ee10c7fdbb6d0b94e |
| S10 | D:\TestPlatform\TapProject\Plugin.DeviceConfig\Model\DeviceConfigModel.cs | 现有表与映射、版本模型 | 4b0022da848a9c0033ae20595e7c93123b952b15096035151f2d3b824f20d980 |
| S11 | D:\TestPlatform\TapProject\Aging.Infrastructure\Aging.Infrastructure.csproj | net472与依赖配置 | f39ad8e25368431e2d2d77c2488c03c027dae5b0812e9dd50ec75de36269cace |
| S12 | D:\06-产品开发\18-舱驾控制器老化设备\老化软件详细开发说明书_V1.0.docx | 本工作区既有详细开发说明参考 | 9c0053ef959a55a15adcf06a7e27ba7cde46ae6f78ecb24291157c49b2d9cef7 |
本文不包含已完成的生产代码、真实设备配置或现场验收结论。方案适用条件和未确认的设备参数在第 21 章列出。